/**
 *	@file	poru_3d.c
 *	@brief	サファリOBJ　3D関連
 *	@author	Miyuki iwasawa
 *	@date	06.03.28
 */

#include "common.h"
#include "system/procsys.h"
#include "system/clact_tool.h"
#include "system/wordset.h"
#include "system/numfont.h"
#include "system/softsprite.h"
#include "poketool/monsno.h"

#include "application/safari_l.h"

#include "safario_sys.h"
#include "safario_sub.h"
#include "safario_gra.naix"
#include "fielddata/build_model/bm_field_id.h"
#include "field/f3d_model_info_def.h"
#include "field/field_3d_anime_local.h"

typedef	struct	{
	fx32			Distance;
	CAMERA_ANGLE	Angle;
	u16				View;
	u16				PerspWay;
	fx32			Near;
	fx32			Far;
	VecFx32			Shift;
}SAFARI_CAMERA_PARAM;

typedef struct{
	u16 distance;
	s16 x,y,z;
}SAFARI_3DOBJ_CAMERA;

static const SAFARI_CAMERA_PARAM FieldCameraData = {
	FX32_CONST(347),//0x15aec1,//0x29aec1,
	{
		-0x229e,0x0,0x0
	},
	GF_CAMERA_PERSPECTIV,
	0x5c1,
	FX32_ONE * 100,
	FX32_ONE * 1200,
	{0x0,0x0,0x0}
};

#include "safario_3d_data.dat"

//======================================================
//プロトタイプ
//======================================================
static void SafariObj_CameraInit(SAFARIO_3DWORK* wk,int heapID);
static void vtcb_3DObjMdlTrans( TCB_PTR tcb, void* p_work );

static void SafariObj_ModelInit(SAFARIO_3DWORK* wk,SAFARI_OBJ_ID id,SAFARI_OBJ_PARAM* prm);
static void SafariObj_ModelRelease(SAFARIO_3DWORK* wk,SAFARIO_3DMDL* p_mdl);
static void SafariObj_ModelDraw(SAFARIO_3DMDL* p_mdl);

static void SafariObj_AnmLoad(SAFARIO_3DWORK* wk,SAFARIO_3DMDL* p_mdl,SAFARIO_3DANM* p_anm,u32 anm_code);
static void SafariObj_AnmDelete(SAFARIO_3DWORK* wk,SAFARIO_3DANM* p_anm);
static void SafariObj_AnmBindAdd(SAFARIO_3DMDL* p_mdl,SAFARIO_3DANM* p_anm);
static void SafariObj_AnmBindRemove(SAFARIO_3DMDL* p_mdl,SAFARIO_3DANM* p_anm);
static void SafariObj_AnmLoop(SAFARIO_3DANM* p_anm);

/**
 *	@brief	3D関連初期化
 */
void SafariObj_3DInit(SAFARIO_3DWORK* wk,u8 anm_time_zone, int heapID)
{
	MI_CpuClear8(wk,sizeof(SAFARIO_3DWORK));

	NNS_G3dInit();		// NitroSystem:３Ｄエンジンの初期化
	G3X_Init();
	G3X_InitMtxStack();
	G3X_SetShading( GX_SHADING_TOON );
	G3X_AlphaTest( FALSE, 0 );
	G3X_AlphaBlend( TRUE );
	G3X_AntiAlias( TRUE );
	G3X_EdgeMarking( TRUE );
	G3X_SetFog( FALSE, GX_FOGBLEND_COLOR_ALPHA, GX_FOGSLOPE_0x8000, 0 );

	G3X_SetClearColor( 0, 0, 0x7fff, 63, FALSE );
	G3_SwapBuffers( GX_SORTMODE_AUTO, GX_BUFFERMODE_W );
	G3_ViewPort( 0, 0, 255, 191 );

	wk->heapID = heapID;
	wk->anm_time_zone = anm_time_zone;

	//テクスチャマネージャ初期化
	{
		int tex_size;

		//リンクドモードの設定
		tex_size = NNS_GfdGetLnkTexVramManagerWorkSize( 128 );
		wk->tex_memory = sys_AllocMemory( wk->heapID, tex_size );

		// マネージャがテクスチャイメージスロットを指定スロット分管理できるようにする
		GF_G3D_InitLnkTexVramManager(
			1024*128, 0, wk->tex_memory, tex_size, TRUE);
	}
	//パレットマネージャー初期化
	{
		int plt_size;

		//リンクドモードの設定
		plt_size = NNS_GfdGetLnkPlttVramManagerWorkSize( 256 );
		wk->plt_memory = sys_AllocMemory( wk->heapID, plt_size );

		// マネージャがテクスチャイメージスロットを指定サイズ分管理できるようにする
		GF_G3D_InitLnkPlttVramManager(
			1024*16, wk->plt_memory, plt_size, TRUE);
	}

	// アロケーター作成
	sys_InitAllocator( &wk->allocator, heapID, 32 );

	//アーカイブデータハンドル取得
	wk->ph_obj = ArchiveDataHandleOpen(ARC_FLD_3D_STRUCT,heapID);
	wk->ph_minfo = ArchiveDataHandleOpen(ARC_BM_INFO_OUT_LIST,heapID);
	wk->ph_anm = ArchiveDataHandleOpen(ARC_BM_ANM,heapID);

	//カメラ初期化
	SafariObj_CameraInit(wk,heapID);

	GF_Disp_GX_VisibleControl( GX_PLANEMASK_BG0, VISIBLE_ON );
    G2_SetBG0Priority(1);
}

/**
 *	@brief	サファリOBJ　3Dメイン
 */
void SafariObj_3DMain(SAFARIO_3DWORK* wk)
{
	GF_G3X_Reset();
	GFC_CameraLookAt();

	NNS_G3dGeFlushBuffer();

	G3_MtxMode( GX_MTXMODE_PROJECTION );
	G3_Identity();
	G3_MtxMode( GX_MTXMODE_POSITION_VECTOR );
	G3_Identity();

	if(wk->draw_f && wk->mdl.valid){
		SafariObj_ModelDraw(&wk->mdl);
	}

	G3_SwapBuffers( GX_SORTMODE_MANUAL, GX_BUFFERMODE_Z );

}

/**
 *	@brief	サファリOBJ　3Dリソース解放
 */
void SafariObj_3DRelease(SAFARIO_3DWORK* wk)
{
	if(wk->mdl.valid){
		SafariObj_ModelRelease(wk,&wk->mdl);
	}
	GFC_FreeCamera(wk->camera);

	sys_FreeMemoryEz( wk->plt_memory );
	sys_FreeMemoryEz( wk->tex_memory );

	//ハンドル閉じる
	ArchiveDataHandleClose( wk->ph_anm );
	ArchiveDataHandleClose( wk->ph_minfo );
	ArchiveDataHandleClose( wk->ph_obj );
}

/**
 *	@brief	オブジェクトチェンジ
 */
void SafariObj_ModelChange(SAFARIO_3DWORK* wk,SAFARI_OBJ_ID id,SAFARI_OBJ_PARAM* prm)
{
	if(wk->mdl.valid){
		SafariObj_ModelRelease(wk,&wk->mdl);
	}
	wk->draw_f = TRUE;
	if(prm == NULL){
		return;
	}
	SafariObj_ModelInit(wk,id,prm);
}

/**
 *	@brief	オブジェクト描画フラグ制御
 */
void SafariObj_ModelVisibleSet(SAFARIO_3DWORK* wk,BOOL flag)
{
	wk->draw_f = flag;
	if(wk->draw_f){
		return;
	}
	GF_G3X_Reset();
	GFC_CameraLookAt();
	NNS_G3dGeFlushBuffer();
	G3_SwapBuffers( GX_SORTMODE_MANUAL, GX_BUFFERMODE_Z );
}

/**
 *	@brief	オブジェクト初期化
 */
static void SafariObj_ModelInit(SAFARIO_3DWORK* wk,SAFARI_OBJ_ID id,SAFARI_OBJ_PARAM* prm)
{
	int i;
	F3D_MDL_INFO info;
	SAFARIO_3DMDL* p_mdl = &wk->mdl;

	// モデルデータ読み込み
	p_mdl->pResMdl = ArcUtil_HDL_Load(wk->ph_obj,
						prm->obj_id, FALSE, wk->heapID, ALLOC_TOP);

	// モデルデータ取得
	p_mdl->pModelSet	= NNS_G3dGetMdlSet( p_mdl->pResMdl );
	p_mdl->pModel		= NNS_G3dGetMdlByIdx( p_mdl->pModelSet, 0 );
	p_mdl->pMdlTex		= NNS_G3dGetTex( p_mdl->pResMdl );

	if( p_mdl->pMdlTex ){
		// テクスチャデータ転送
//		VWaitTCB_Add( vtcb_3DObjMdlTrans, p_mdl, 0);
		LoadVRAMTexture( p_mdl->pMdlTex );
		BindTexture( p_mdl->pResMdl, p_mdl->pMdlTex );
	}

	//描画オブジェクト初期化
	NNS_G3dRenderObjInit( &p_mdl->render, p_mdl->pModel );

	DC_FlushAll();
	p_mdl->valid = TRUE;

	//指定ＯＢＪ情報をアーカイバから入手
	ArchiveDataLoadByHandle(wk->ph_minfo, prm->obj_id, &info);
	if(info.Flg && info.Type != TYPE_TIME_ANIME){
		u32 anm_code;
#if 0
		if(info.Type == TYPE_TIME_ANIME){	//アニメセット
			anm_code = info.Code[wk->anm_time_zone];
			SafariObj_AnmLoad(wk,p_mdl,&p_mdl->anm[p_mdl->anm_num],anm_code);
			SafariObj_AnmBindAdd(p_mdl,&p_mdl->anm[p_mdl->anm_num]);
			p_mdl->anm_num++;
		}else{
			for(i=0;i<ONE_MODEL_ANM_NUM_MAX;i++){
				anm_code = info.Code[i];
				if (anm_code == ANIME_NONE_CODE){
					break;//処理を抜ける
				}
				SafariObj_AnmLoad(wk,p_mdl,&p_mdl->anm[p_mdl->anm_num],anm_code);
				SafariObj_AnmBindAdd(p_mdl,&p_mdl->anm[p_mdl->anm_num]);
				p_mdl->anm_num++;
			}
		}
#else
		for(i=0;i<ONE_MODEL_ANM_NUM_MAX;i++){
			anm_code = info.Code[i];
			if (anm_code == ANIME_NONE_CODE){
				break;//処理を抜ける
			}
			SafariObj_AnmLoad(wk,p_mdl,&p_mdl->anm[p_mdl->anm_num],anm_code);
			SafariObj_AnmBindAdd(p_mdl,&p_mdl->anm[p_mdl->anm_num]);
			p_mdl->anm_num++;
		}
#endif
	}

	//カメラセット
	{
		const SAFARI_3DOBJ_CAMERA* cp = &DATA_Safari3DOBJ_Camera[id];

		wk->target.x = FX32_CONST(cp->x);
		wk->target.y = FX32_CONST(cp->y);
		wk->target.z = FX32_CONST(cp->z);
		GFC_SetCameraDistance(FX32_CONST(cp->distance),wk->camera);

#ifdef PM_DEBUG
		wk->d_target = wk->target;
		wk->d_distance = FX32_CONST(cp->distance);
#endif
	}
}

/*
 *	@brief	オブジェクト解放
 */
static void SafariObj_ModelRelease(SAFARIO_3DWORK* wk,SAFARIO_3DMDL* p_mdl)
{
	int i;
	NNSG3dTexKey texKey;
	NNSG3dTexKey tex4x4Key;
	NNSG3dPlttKey plttKey;

	for(i = 0;i < p_mdl->anm_num;i++){
		SafariObj_AnmBindRemove(p_mdl,&p_mdl->anm[i]);
		SafariObj_AnmDelete(wk,&p_mdl->anm[i]);
	}
	if( p_mdl->pMdlTex ){
		//VramKey破棄
		NNS_G3dTexReleaseTexKey( p_mdl->pMdlTex, &texKey, &tex4x4Key );
		NNS_GfdFreeTexVram( texKey );
		NNS_GfdFreeTexVram( tex4x4Key );

		plttKey = NNS_G3dPlttReleasePlttKey( p_mdl->pMdlTex );
		NNS_GfdFreePlttVram( plttKey );
	}

	// 全リソース破棄
	if(p_mdl->pResMdl){
		sys_FreeMemoryEz( p_mdl->pResMdl );
	}
	MI_CpuClear8( p_mdl, sizeof(SAFARIO_3DMDL) );
}

/**
 *	@brief	サファリOBJ モデル描画
 */
static void SafariObj_ModelDraw(SAFARIO_3DMDL* p_mdl)
{
	int i;
	MtxFx33 mtx;
	MtxFx33 calc_mtx;
	VecFx32 matrix;	// 座標
	VecFx32 scale;	// 拡縮

	//アニメ再生
	for(i = 0;i < p_mdl->anm_num;i++){
		SafariObj_AnmLoop(&p_mdl->anm[i]);
	}

	VEC_Set(&matrix,0,0,0);
	VEC_Set(&scale,FX32_ONE,FX32_ONE,FX32_ONE);

	// 回転行列作成
	MTX_Identity33( &mtx );
#if 0
	MTX_RotX33( &calc_mtx, FX_SinIdx( p_draw->rota[FLD_3DROTA_WAY_X] ), FX_CosIdx( p_draw->rota[FLD_3DROTA_WAY_X] ) );
	MTX_Concat33( &calc_mtx, &mtx, &mtx );
	MTX_RotZ33( &calc_mtx, FX_SinIdx( p_draw->rota[FLD_3DROTA_WAY_Z] ), FX_CosIdx( p_draw->rota[FLD_3DROTA_WAY_Z] ) );
	MTX_Concat33( &calc_mtx, &mtx, &mtx );
	MTX_RotY33( &calc_mtx, FX_SinIdx( p_draw->rota[FLD_3DROTA_WAY_Y] ), FX_CosIdx( p_draw->rota[FLD_3DROTA_WAY_Y] ) );
	MTX_Concat33( &calc_mtx, &mtx, &mtx );
#endif
	simple_3DModelDraw( &p_mdl->render, &matrix, &mtx, &scale );
}

/**
 *	@brief	サファリOBJ アニメロード
 */
static void SafariObj_AnmLoad(SAFARIO_3DWORK* wk,SAFARIO_3DMDL* p_mdl,SAFARIO_3DANM* p_anm,u32 anm_code)
{
	void* p_data = ArcUtil_HDL_Load( wk->ph_anm, anm_code, FALSE, wk->heapID, ALLOC_TOP );

	p_anm->pResAnm = p_data;
	p_anm->pOneResAnm = NNS_G3dGetAnmByIdx(p_anm->pResAnm,0);

	//アニメオブジェクトのメモリ確保
	p_anm->pAnmObj = NNS_G3dAllocAnmObj(&wk->allocator,p_anm->pOneResAnm,p_mdl->pModel);
	//アニメオブジェクト初期化
	NNS_G3dAnmObjInit(p_anm->pAnmObj,p_anm->pOneResAnm,p_mdl->pModel,p_mdl->pMdlTex);
}

/**
 *	@brief	サファリOBJ アニメ破棄
 */
static void SafariObj_AnmDelete(SAFARIO_3DWORK* wk,SAFARIO_3DANM* p_anm)
{
	if(p_anm->pResAnm == NULL)	{
		return;
	}
	NNS_G3dFreeAnmObj(&wk->allocator,p_anm->pAnmObj);
	sys_FreeMemoryEz(p_anm->pResAnm);
	MI_CpuClear8(p_anm,sizeof(SAFARIO_3DANM));
}

/**
 *	@brief	サファリOBJ アニメリンク
 */
static void SafariObj_AnmBindAdd(SAFARIO_3DMDL* p_mdl,SAFARIO_3DANM* p_anm)
{
	NNS_G3dRenderObjAddAnmObj(&p_mdl->render,p_anm->pAnmObj);
}
/**
 *	@brief	サファリOBJ アニメリンク破棄
 */
static void SafariObj_AnmBindRemove(SAFARIO_3DMDL* p_mdl,SAFARIO_3DANM* p_anm)
{
	NNS_G3dRenderObjRemoveAnmObj(&p_mdl->render,p_anm->pAnmObj);
}
/**
 *	@brief	サファリOBJ アニメループ再生
 */
static void SafariObj_AnmLoop(SAFARIO_3DANM* p_anm)
{
	fx32 end_frame = NNS_G3dAnmObjGetNumFrame( p_anm->pAnmObj );

	p_anm->frame = (p_anm->frame + FX32_ONE) % end_frame;
	NNS_G3dAnmObjSetFrame( p_anm->pAnmObj, p_anm->frame );
}

/**
 *	@brief	サファリOBJ　カメラ設定
 */
static void SafariObj_CameraInit(SAFARIO_3DWORK* wk,int heapID)		// カメラ
{
	const SAFARI_CAMERA_PARAM * camera_param = &FieldCameraData;

	wk->camera = GFC_AllocCamera(heapID);

	wk->target.x = FX32_CONST(0);
	wk->target.y = FX32_CONST(0);
	wk->target.z = FX32_CONST(0);

	//カメラセット
	GFC_InitCameraTDA( &wk->target,
				camera_param->Distance,	// 距離
				&camera_param->Angle,	//
				camera_param->PerspWay,	// 投射影の角度
				camera_param->View,
				TRUE,
				wk->camera
				);
	//カメラの有効化
	GFC_AttachCamera(wk->camera);
	//ニア・ファー設定
	GFC_SetCameraClip(camera_param->Near, camera_param->Far,wk->camera);
	//カメラシフト
	GFC_ShiftCamera(&camera_param->Shift, wk->camera);

#if 0
	VecFx32	camera_pos = {0,0,0x10000};
	CAMERA_ANGLE angle = {0,0,0};
	fx32	distance   = 0x10000;
	u16		perspway   = 5460+2000;

	wk->camera = GFC_AllocCamera(heapID);

	{
		VecFx32 tv = {0,FX32_CONST(5),FX32_CONST(-10)};
		VecFx32 pos = {0,FX32_CONST(50),FX32_CONST(55)};
		GFC_InitCameraTC( &tv,&pos,perspway,GF_CAMERA_PERSPECTIV,FALSE,wk->camera);
	}
	GFC_SetCameraClip(FX32_CONST(1),FX32_CONST(2048),wk->camera);
	GFC_PurgeCameraTarget(wk->camera);
	GFC_AttachCamera(wk->camera);
#elsif 0
	u16		perspway   = 0x05c1;

	wk->camera = GFC_AllocCamera(heapID);

	{
		VecFx32 tv = {0,FX32_CONST(5),0};
		VecFx32 pos = {0,FX32_CONST(1000),FX32_CONST(240)};
		GFC_InitCameraTC( &tv,&pos,perspway,GF_CAMERA_PERSPECTIV,FALSE,wk->camera);
	}
	GFC_SetCameraClip(0,FX32_CONST(200),wk->camera);
	GFC_PurgeCameraTarget(wk->camera);
	GFC_AttachCamera(wk->camera);
#endif
}

//----------------------------------------------------------------------------
/**
 *	@brief	モデル転送タスク
 *
 *	@param	tcb		TCBワーク
 *	@param	p_work	ワーク
 *
 *	@return	none
 */
//-----------------------------------------------------------------------------
static void vtcb_3DObjMdlTrans( TCB_PTR tcb, void* p_work )
{
	SAFARIO_3DMDL* p_mdl = (SAFARIO_3DMDL*)p_work;

	if(p_mdl->pMdlTex != NULL){
		// テクスチャデータを転送
		LoadVRAMTexture( p_mdl->pMdlTex );
		BindTexture( p_mdl->pResMdl, p_mdl->pMdlTex );
	}
	TCB_Delete( tcb );
}

#ifdef PM_DEBUG
BOOL Debug_SafariObj_CameraSet(SAFARIO_3DWORK* wk)
{
	BOOL flag = FALSE;
	u8 dif = 10;
	if(!(sys.cont & PAD_BUTTON_R)){
		return FALSE;
	}
	if((sys.cont & PAD_BUTTON_L)){
		dif = 1;
	}
	if(sys.repeat & PAD_KEY_LEFT){
		wk->d_distance -= FX32_CONST(dif);
		flag = TRUE;
	}else if(sys.repeat & PAD_KEY_RIGHT){
		wk->d_distance += FX32_CONST(dif);
		flag = TRUE;
	}else if(sys.repeat & PAD_KEY_UP){
		wk->d_target.y -= FX32_CONST(dif);
		flag = TRUE;
	}else if(sys.repeat & PAD_KEY_DOWN){
		wk->d_target.y += FX32_CONST(dif);
		flag = TRUE;
	}else if(sys.repeat & PAD_BUTTON_X){
		wk->d_target.z -= FX32_CONST(dif);
		flag = TRUE;
	}else if(sys.repeat & PAD_BUTTON_Y){
		wk->d_target.z += FX32_CONST(dif);
		flag = TRUE;
	}else if(sys.repeat & PAD_BUTTON_A){
		wk->d_target.x -= FX32_CONST(dif);
		flag = TRUE;
	}else if(sys.repeat & PAD_BUTTON_B){
		wk->d_target.x += FX32_CONST(dif);
		flag = TRUE;
	}
	if(!flag){
		return FALSE;
	}
	wk->target = wk->d_target;
	GFC_SetCameraDistance(wk->d_distance,wk->camera);
	OS_Printf("カメラ 距離=%d, 注視点(%d,%d,%d)\n",FX_Whole(wk->d_distance),
			FX_Whole(wk->d_target.x),
			FX_Whole(wk->d_target.y),
			FX_Whole(wk->d_target.z)
			);
	return TRUE;
}
#endif